/*
    Copyright (C) <2010>  <Markus Scharnowski markus.scharnowski@gmail.com>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#include "timekeeper.h"
#include <sstream>
#include <iomanip>

using namespace std;

TimeKeeper::TimeKeeper()
{
  dateForStrings = false;
  start();
  isRunning = false;
}

int TimeKeeper::start()
{
  gettimeofday(&tvPrev,NULL);
  gettimeofday(&tvLast,NULL);
  gettimeofday(&tvStart,NULL);
  gettimeofday(&tvStop,NULL);
  timeStart = time(NULL);
  timeStop  = timeStart;
  timePrev  = timeStart;
  timeLast  = timeStart;
  pauseTimeSec = 0;
  pauseTotalTimeSec = 0;
  isRunning = true;
  isPauseActive = false;
  return EXIT_SUCCESS;
}

int TimeKeeper::stop()
{
  if(false == isRunning)
  {
    return EXIT_FAILURE;
  }

  stopPause();
  timeStop = time(NULL);
  timeLast = timeStop;
  gettimeofday(&tvLast,NULL);
  gettimeofday(&tvStop,NULL);
  isRunning = false;

  return EXIT_SUCCESS;
}

int TimeKeeper::update()
{
  if(false == isRunning)
  {
    return EXIT_FAILURE;
  }

  stopPause();
  timePrev = timeLast;
  timeLast = time(NULL);

  tvPrev = tvLast;
  gettimeofday(&tvLast,NULL);

  setPrevToStart(getLastToStart());
  setLastToStart(calculateLastToStart());

  return EXIT_SUCCESS;
}

double TimeKeeper::getDelta(struct timeval tvLater, struct timeval tvEarlier)
{
  double tvTime = (double)(tvLater.tv_sec - tvEarlier.tv_sec);

  tvTime = tvTime*1e6+(double)(tvLater.tv_usec - tvEarlier.tv_usec);
  tvTime /=1e6;

  return tvTime;
}

/*********************************************************
* Functions for getting from external
**********************************************************/
double TimeKeeper::getDeltaLastToStart()
{
  return getLastToStart();
}
double TimeKeeper::getDeltaPrevToStart()
{
  return getPrevToStart();
}

/*********************************************************
* Functions for getting from internal
**********************************************************/
double TimeKeeper::getLastToStart()
{
  return secLastToStart;
}
double TimeKeeper::getPrevToStart()
{
  return secPrevToStart;
}

/*********************************************************
* Functions for setting
**********************************************************/
double TimeKeeper::setLastToStart(double d)
{
  return (secLastToStart = d);
}
double TimeKeeper::setPrevToStart(double d)
{
  return (secPrevToStart = d);
}

/* Important calculation function ^^ */
double TimeKeeper::calculateLastToStart()
{
  double retval = 0;

  retval = getDelta(tvLast,tvStart);
  retval -= pauseTotalTimeSec;

  return retval;
}

/*********************************************************
* Functions for getting from external
**********************************************************/
double TimeKeeper::getDeltaNowToStart()
{
  double retval = 0;
  struct timeval now;

  gettimeofday(&now,NULL);

  if(false == isRunning)
  {
    retval = getDelta(tvStop,tvStart);
  }
  else
  {
    retval = getDelta(now,tvStart);
  }
  retval -= pauseTotalTimeSec;

  return retval;
}

double TimeKeeper::getDeltaNowToLast()
{
  double retval = 0;
  retval = getDeltaNowToStart()-getLastToStart();
  return retval;
}

double TimeKeeper::getDeltaLastToPrev()
{
  double retval = 0;
  retval = getLastToStart()-getPrevToStart();
  return retval;
}

double TimeKeeper::getDeltalNowToPrev()
{
  double retval = 0;
  retval = getDeltaNowToStart()-getPrevToStart();
  return retval;
}

/*********************************************************
 * String getting for external
**********************************************************/
string TimeKeeper::makeString(time_t timer)
{
  stringstream sstr;
  struct tm *helper=NULL;

  helper=localtime(&timer);
  sstr << setfill('0');
  if(true == dateForStrings)
  {
    sstr << setw(2) << 1900+helper->tm_year << "-";
    sstr << setw(2) << helper->tm_mon << "-";
    sstr << setw(2) << helper->tm_mday << " ";
  }
  sstr << setw(2) << helper->tm_hour << ":";
  sstr << setw(2) << helper->tm_min << ":";
  sstr << setw(2) << helper->tm_sec;

  return sstr.str();
}

string TimeKeeper::getStartTimeString()
{
  return makeString(timeStart);
}

string TimeKeeper::getPreviousTimeString()
{
  return makeString(timePrev);
}

string TimeKeeper::getLastTimeString()
{
  return makeString(timeLast);
}

string TimeKeeper::getNowTimeString()
{
  time_t timeNow = time(NULL);
  return makeString(timeNow);
}


/*********************************************************
 * Pause handling
**********************************************************/
int TimeKeeper::pause()
{
  if(false == isRunning)
  {
    return EXIT_FAILURE;
  }

  startPause();

  return EXIT_SUCCESS;
}

int TimeKeeper::startPause()
{
  if(true == isPauseActive)
  {
    return stopPause();
  }
  gettimeofday(&tvPauseStart,NULL);
  isPauseActive = true;

  return EXIT_SUCCESS;
}

int TimeKeeper::stopPause()
{
  if(false == isPauseActive)
  {
    return EXIT_FAILURE;
  }

  gettimeofday(&tvPauseStop,NULL);
  pauseTimeSec = getDelta(tvPauseStop,tvPauseStart);
  pauseTotalTimeSec += pauseTimeSec;
  isPauseActive = false;

  return EXIT_SUCCESS;
}

/*********************************************************
  Status information
  ********************************************************/
bool TimeKeeper::isPaused()
{
  return isPauseActive;
}

bool TimeKeeper::isActive()
{
  return isRunning;
}

/*********************************************************
 * Date string handling
**********************************************************/
bool TimeKeeper::setDateForStrings(bool b)
{
  return (dateForStrings = b);
}

bool TimeKeeper::isDateForStringsEnabled()
{
  return dateForStrings;
}
